package com.vca.service.service.Impl;

import java.io.UnsupportedEncodingException;
import java.math.RoundingMode;
import java.util.Date;

import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.Lists;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.vca.common.constants.Constants;
import com.vca.common.exception.VcaException;
import com.vca.common.model.course.CourseScheduling;
import com.vca.common.model.exceptions.ThirdPartyExceptions;
import com.vca.common.model.invoice.InvoiceCreditNote;
import com.vca.common.model.invoice.InvoiceOrderRelation;
import com.vca.common.model.invoice.InvoiceRecord;
import com.vca.common.model.order.StoreOrder;
import com.vca.common.model.order.StoreOrderInfo;
import com.vca.common.model.user.User;
import com.vca.common.model.vca_product.VcaProduct;
import com.vca.common.model.vca_product.VcaProductAttrValue;
import com.vca.common.request.InvoicePageRequest;
import com.vca.common.request.InvoiceRequest;
import com.vca.common.request.SchedulingAdminListRequest;
import com.vca.common.response.*;
import com.vca.common.utils.*;
import com.vca.common.vo.*;
import com.vca.service.dao.invoice.InvoiceRecordDao;
import com.vca.service.service.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Li
 * @description
 * @date 2023-02-08 16:20
 */

@Service
@Slf4j
public class InvoiceRecordServiceImpl extends ServiceImpl<InvoiceRecordDao, InvoiceRecord> implements InvoiceRecordService {

    @Autowired
    private RestTemplateUtil restTemplateUtil;

    @Autowired
    private StoreOrderInfoService orderInfoService;

    @Autowired
    private StoreOrderService orderService;

    @Autowired
    private InvoiceOrderRelationService invoiceOrderRelationService;

    @Autowired
    private UserService userService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private InvoiceCreditNoteService invoiceCreditNoteService;

    @Autowired
    private SystemConfigService systemConfigService;

    @Autowired
    private ThirdPartyExceptionsService thirdPartyExceptionsService;

    @Autowired
    private SystemGroupDataService systemGroupDataService;

    @Autowired
    private VcaProductService vcaProductService;

    //接口地址
    private final static String SETTLEMENT_UPLOAD="/settlementUpload";

    @Value("${invoice.url}")
    private String url;

    //开发地址
    private final static String DEV="http://richemont-test.xforceplus.com";

    //生产地址
    private final static String PROD="https://richemont.xforceplus.com";


    /**
     * @param request
     * @return {@link Boolean}
     * @description 申请发票
     * @author Li
     * @date 2023/2/8 11:03
     */
    @Override
    @SneakyThrows
    public Long settlementUpload(InvoiceRequest request) {
        User user = userService.getInfo();
        HashMap<String, String> info = systemConfigService.info(168);
        //查询需要开票订单
        List<StoreOrder> orders = orderService.list(new LambdaQueryWrapper<StoreOrder>()
                .in(StoreOrder::getOrderId, request.getOrderNos())
                .ne(StoreOrder::getRefundStatus, 2)
                .eq(StoreOrder::getIsDel, 0)
                .eq(StoreOrder::getInvoicingStatus, 0)
                .gt(StoreOrder::getPayPrice, 0.09));
        List<StoreOrder> orderByUid = orders.stream().filter(e -> e.getUid().equals(user.getUid())).collect(Collectors.toList());
        if (ObjectUtil.isEmpty(orders)||orderByUid.size()<orders.size()) {
            throw new VcaException("请选择可开票订单");
        }
        List<StoreOrderInfo> infos = orderInfoService.list(Wrappers.<StoreOrderInfo>lambdaQuery().in(StoreOrderInfo::getOrderNo, request.getOrderNos()));
        XforceVo xforce = getXforce(infos,orders,1);
        //组合开票请求数据
        String orderNo = VcaUtil.getOrderNo(info.get("xforceNo"));
        xforce.setOrderInfoNo(orderNo);
        xforce.setStoreCode(info.get("storeCode"));
        xforce.setBrand(ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish("lecole")));
        xforce.setPosDate(DateUtil.nowDate());
        xforce.setOrderType("SALE");
        xforce.setInvoiceType("ce");
        xforce.setBusinessBillType("AR");
        xforce.setStatus("1");
        xforce.setSellerInfoFill("0");
        xforce.setPurchaserInfoFill("1");
        xforce.setEmail(ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish(request.getEmail())));
        xforce.setPurchaserTaxNo(request.getType().equals(2) ? request.getCompanyTaxNumber() : "");
        xforce.setPurchaserName(request.getType().equals(2) ? ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish(request.getCorporateName())) : ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish(request.getName())));
        JSONObject object = new JSONObject();
        object.put("mainInfo", JSON.parseObject(JSON.toJSONString(xforce)));
        //保存发票信息
        InvoiceRecord invoiceRecord = new InvoiceRecord();
        invoiceRecord.setUid(user.getUid());
        invoiceRecord.setInvoiceOrderId(orderNo);
        invoiceRecord.setType(request.getType());
        invoiceRecord.setName(request.getType().equals(1) ? request.getName() : "");
        invoiceRecord.setCorporateName(request.getType().equals(2) ? request.getCorporateName() : "");
        invoiceRecord.setCompanyTaxNumber(request.getType().equals(2) ? request.getCompanyTaxNumber() : "");
        invoiceRecord.setPrice(xforce.getPayAmount());
        invoiceRecord.setEmail(request.getEmail());
        invoiceRecord.setCreditNote(0);
        invoiceRecord.setUrl("");
        invoiceRecord.setOrderInfoTotal(xforce.getDetailInfo().size());
        invoiceRecord.setStatus(0);
        List<InvoiceOrderRelation> relations = new ArrayList<>();
        //先插入数据 防止回调速度过快
        Boolean execute = transactionTemplate.execute(e -> {
            this.save(invoiceRecord);
            orders.forEach(o -> {
                InvoiceOrderRelation invoiceOrderRelation = new InvoiceOrderRelation();
                invoiceOrderRelation.setInvoiceId(invoiceRecord.getId());
                invoiceOrderRelation.setOrderId(o.getId());
                relations.add(invoiceOrderRelation);
            });
            invoiceOrderRelationService.saveBatch(relations);
            //修改订单 开票状态  0=未申请 1=已申请
            orderService.update(Wrappers.<StoreOrder>lambdaUpdate().in(StoreOrder::getId, orders.stream().map(StoreOrder::getId).collect(Collectors.toList())).set(StoreOrder::getInvoicingStatus, 1));
            return Boolean.TRUE;
        });
        log.info("xforce----------------------->{}", object);
        JSONObject jsonObject = JSON.parseObject(null);
        try {
            if (execute) {
                jsonObject = JSON.parseObject(restTemplateUtil.postAuthData(url+SETTLEMENT_UPLOAD, object, info.get("userName"), info.get("password")));
            }
        }catch (Exception e){
            //开票失败回滚
            rollback(invoiceRecord,relations,orders);
            throw new VcaException("开票失败，请重新开票");
        }finally {
            log.info("jsonObject----------------------->{}", jsonObject);
        }
        //判断是否开票成功 开票失败回滚
        if (ObjectUtil.isEmpty(jsonObject)) {
            rollback(invoiceRecord,relations,orders);
        }
        //判断是否开票成功 开票失败回滚
        if (!"0".equals(jsonObject.get("code").toString())) {
            String message = jsonObject.get("message").toString();
            rollback(invoiceRecord,relations,orders);
            throw new VcaException(message);
        }
        return ObjectUtil.isEmpty(jsonObject) ? 0L : invoiceRecord.getId();
    }

    /**
     * @return {@link List}<{@link InvoicedListResponse}>
     * @description 查询可开票列表
     * * @param orderNo
     * @author Li
     * @date 2023/2/9 16:44
     */
    @Override
    public List<InvoicedListResponse> getInvoiceList(String language,String orderNo) {
        User user = userService.getInfo();
        if (ObjectUtil.isNotEmpty(orderNo)) {
            StoreOrder order = orderService.getOne(Wrappers.<StoreOrder>lambdaQuery().eq(StoreOrder::getPaid, 1)
                    .eq(StoreOrder::getOrderId, orderNo)
                    .ne(StoreOrder::getRefundStatus, 2)
                    .eq(StoreOrder::getIsDel, 0)
                    .eq(StoreOrder::getUid, user.getUid())
                    .eq(StoreOrder::getInvoicingStatus, 0)
                    .gt(StoreOrder::getPayPrice, 0.09));
            if (ObjectUtil.isEmpty(order)) {
                if("en".equals(language)){
                    throw new VcaException("该订单不符合开票条件");//The order is not eligible for billing
                }else{
                    throw new VcaException("该订单不符合开票条件");
                }

            }
        }
        List<InvoicedListResponse> responses = new ArrayList<>();
        List<StoreOrder> orderList = orderService.list(
                Wrappers.<StoreOrder>lambdaQuery().eq(StoreOrder::getPaid, 1)
                        .ne(StoreOrder::getRefundStatus, 2)
                        .eq(StoreOrder::getIsDel, 0)
                        .eq(StoreOrder::getUid, user.getUid())
                        .eq(StoreOrder::getInvoicingStatus, 0)
                        .gt(StoreOrder::getPayPrice, 0.09));
        if (ObjectUtil.isEmpty(orderList)) {
            return responses;
        }
        Map<String, List<StoreOrderInfo>> listMap = orderInfoService.list(Wrappers.<StoreOrderInfo>lambdaQuery().in(StoreOrderInfo::getOrderNo, orderList.stream().map(StoreOrder::getOrderId).collect(Collectors.toList()))).stream().collect(Collectors.groupingBy(StoreOrderInfo::getOrderNo));
        orderList.forEach(e -> {
            InvoicedListResponse response = new InvoicedListResponse();
            response.setSort(0);
            if (ObjectUtil.isNotEmpty(orderNo)) {
                response.setSort(e.getOrderId().equals(orderNo) ? 1 : 0);
            }
            response.setOrderNo(e.getOrderId());
            response.setPayTime(e.getPayTime());
            response.setPayPrice(e.getPayPrice());
            StringBuilder stringBuilder = new StringBuilder();
            if (!e.getType().equals(1)) {
                for (int i = 0; i < listMap.get(e.getOrderId()).size(); i++) {
                    if("en".equals(language)){
                        stringBuilder.append(i == listMap.get(e.getOrderId()).size() - 1 ? listMap.get(e.getOrderId()).get(i).getNameEn() : listMap.get(e.getOrderId()).get(i).getNameEn() + "/");
                    }else{
                        stringBuilder.append(i == listMap.get(e.getOrderId()).size() - 1 ? listMap.get(e.getOrderId()).get(i).getName() : listMap.get(e.getOrderId()).get(i).getName() + "/");
                    }
                }
                response.setName(stringBuilder.toString());
            }else {
                if("en".equals(language)){
                    response.setName(listMap.get(e.getOrderId()).get(0).getNameEn());
                }else{
                    response.setName(listMap.get(e.getOrderId()).get(0).getName());
                }
            }
            BigDecimal max = Collections.max(listMap.get(e.getOrderId()).stream().map(StoreOrderInfo::getPrice).collect(Collectors.toList()));
            if (max.compareTo(new BigDecimal("0.09"))>0) {
                responses.add(response);
            }
        });
        //先根据支付时间排序 再根据sort字段排序
        return responses.stream()
                .sorted(Comparator.comparing(InvoicedListResponse::getPayTime).reversed())
                .collect(Collectors.toList()).stream()
                .sorted(Comparator.comparing(InvoicedListResponse::getSort).reversed())
                .collect(Collectors.toList());
    }

    @Override
    public List<InvoicedRecordResponse> getInvoicedRecord() {
        List<InvoicedRecordResponse> responses = new ArrayList<>();
        User user = userService.getInfo();
        List<InvoiceRecord> records = this.list(Wrappers.<InvoiceRecord>lambdaQuery().eq(InvoiceRecord::getUid, user.getUid()));
        if (ObjectUtil.isEmpty(records)) {
            return responses;
        }
        List<InvoiceOrderRelation> relations = invoiceOrderRelationService.list(Wrappers.<InvoiceOrderRelation>lambdaQuery()
                .in(InvoiceOrderRelation::getInvoiceId, records.stream().map(InvoiceRecord::getId).collect(Collectors.toList())));
        if (ObjectUtil.isEmpty(relations)) {
            return responses;
        }
        List<StoreOrder> orderList = orderService.list(Wrappers.<StoreOrder>lambdaQuery().in(StoreOrder::getId, relations.stream().map(InvoiceOrderRelation::getOrderId).collect(Collectors.toList())));
        Map<Integer, List<StoreOrder>> orderMap = orderList.stream().collect(Collectors.groupingBy(StoreOrder::getId));
        Map<String, List<StoreOrderInfo>> listMap = orderInfoService.list(Wrappers.<StoreOrderInfo>lambdaQuery().in(StoreOrderInfo::getOrderNo, orderList.stream().map(StoreOrder::getOrderId).collect(Collectors.toList()))).stream().collect(Collectors.groupingBy(StoreOrderInfo::getOrderNo));
        Map<Long, List<InvoiceOrderRelation>> relationMap = relations.stream().collect(Collectors.groupingBy(InvoiceOrderRelation::getInvoiceId));
        records.forEach(e -> {
            if (ObjectUtil.isNotEmpty(relationMap.get(e.getId()))) {
                StringBuilder stringBuilder = new StringBuilder();
                InvoicedRecordResponse response = new InvoicedRecordResponse();
                for (int i = 0; i < relationMap.get(e.getId()).size(); i++) {
                    InvoiceOrderRelation relation = relationMap.get(e.getId()).get(i);
                    orderMap.get(relation.getOrderId()).forEach(o -> {
                        for (int j = 0; j < listMap.get(o.getOrderId()).size(); j++) {
                            stringBuilder.append(j == listMap.get(o.getOrderId()).size() - 1 ? listMap.get(o.getOrderId()).get(j).getName() : listMap.get(o.getOrderId()).get(j).getName() + "/");
                        }
                    });
                }
                response.setInvoiceTime(e.getCreateTime());
                response.setInvoicePrice(e.getPrice());
                response.setInvoiceId(e.getId());
                response.setStatus(e.getStatus());
                response.setName(stringBuilder.toString());
                response.setStatus(ObjectUtil.isEmpty(e.getUrl()) ? 0 : e.getStatus());
                responses.add(response);
            }
        });
        //根据开票日期排序
        return responses.stream().sorted(Comparator.comparing(InvoicedRecordResponse::getInvoiceTime).reversed()).collect(Collectors.toList());
    }

    /**
     * @param invoiceId
     * @return {@link InvoicedDetailsResponse}
     * @description 发票详情
     * @author Li
     * @date 2023/2/10 14:59
     */
    @Override
    public InvoicedDetailsResponse getInvoiceDetails(Integer invoiceId) {

        User user = userService.getInfo();
        InvoicedDetailsResponse response = new InvoicedDetailsResponse();

        InvoiceRecord record = this.getOne(Wrappers.<InvoiceRecord>lambdaQuery().eq(InvoiceRecord::getId, invoiceId).eq(InvoiceRecord::getUid,user.getUid()));
        if (ObjectUtil.isEmpty(record)) {
            throw new VcaException("未查询到该发票");
        }
        List<InvoiceOrderRelation> relations = invoiceOrderRelationService.list(Wrappers.<InvoiceOrderRelation>lambdaQuery()
                .eq(InvoiceOrderRelation::getInvoiceId, invoiceId));
        if (ObjectUtil.isEmpty(relations)) {
            throw new VcaException("未查询到该发票");
        }
        List<StoreOrder> orderList = orderService.list(Wrappers.<StoreOrder>lambdaQuery().in(StoreOrder::getId, relations.stream().map(InvoiceOrderRelation::getOrderId).collect(Collectors.toList())));
        Map<String, List<StoreOrderInfo>> listMap = orderInfoService.list(Wrappers.<StoreOrderInfo>lambdaQuery().in(StoreOrderInfo::getOrderNo, orderList.stream().map(StoreOrder::getOrderId).collect(Collectors.toList()))).stream().collect(Collectors.groupingBy(StoreOrderInfo::getOrderNo));
        List<InvoicedListResponse> orders = new ArrayList<>();
        orderList.forEach(e -> {
            InvoicedListResponse invoicedListResponse = new InvoicedListResponse();
            invoicedListResponse.setSort(0);
            invoicedListResponse.setOrderNo(e.getOrderId());
            invoicedListResponse.setPayTime(e.getPayTime());
            invoicedListResponse.setPayPrice(e.getPayPrice());
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < listMap.get(e.getOrderId()).size(); i++) {
                stringBuilder.append(i == listMap.get(e.getOrderId()).size() - 1 ? listMap.get(e.getOrderId()).get(i).getName() : listMap.get(e.getOrderId()).get(i).getName() + "/");
            }
            invoicedListResponse.setName(stringBuilder.toString());
            orders.add(invoicedListResponse);
        });
        response.setOrderList(orders);
        BeanUtils.copyProperties(record, response);
        response.setStatus(ObjectUtil.isEmpty(response.getUrl()) ? 0 : response.getStatus());
        return response;
    }

    /**
     * @param request
     * @return {@link PageInfo}<{@link InvoicePageResponse}>
     * @description 发票分页
     * @author Li
     * @date 2023/2/10 18:32
     */
    @Override
    public PageInfo<InvoicePageResponse> invoicePage(InvoicePageRequest request) {
        List<InvoicePageResponse> responses = new ArrayList<>();
        LambdaQueryWrapper<InvoiceRecord> wrapper = new LambdaQueryWrapper<>();
        if (ObjectUtil.isNotEmpty(request.getStatus())){
            if (request.getStatus().equals("0")){
                wrapper.in( InvoiceRecord::getStatus, 0,1);
                wrapper.isNull(InvoiceRecord::getUrl).or().eq(InvoiceRecord::getUrl,"");
            }else {
                wrapper.eq(ObjectUtil.isNotEmpty(request.getStatus()), InvoiceRecord::getStatus, request.getStatus());
            }

        }
        wrapper.eq(ObjectUtil.isNotEmpty(request.getType()), InvoiceRecord::getType, request.getType());
        if (ObjectUtil.isNotEmpty(request.getNikeName())) {
            List<User> users = userService.list(Wrappers.<User>lambdaQuery().like(User::getNickname, request.getNikeName()));
            if (ObjectUtil.isEmpty(users)) {
                return PageUtils.startPage(responses, request.getLimit(), request.getPage());
            }
            wrapper.in(InvoiceRecord::getUid, users.stream().map(User::getUid).collect(Collectors.toList()));
        }

        getRequestTimeWhere(wrapper, request);
        wrapper.apply(ObjectUtil.isNotEmpty(request.getKeywords()), "CONCAT(IFNULL( `name`, '' ),IFNULL( `corporate_name`, '' ),IFNULL( `company_tax_number`, '' ) ) LIKE '%" + request.getKeywords() + "%'");
        List<InvoiceRecord> records = this.list(wrapper);
        if (ObjectUtil.isEmpty(records)) {
            return PageUtils.startPage(responses, request.getLimit(), request.getPage());
        }
        Map<Integer, List<User>> userList = userService.list(Wrappers.<User>lambdaQuery().in(User::getUid, records.stream().map(InvoiceRecord::getUid).collect(Collectors.toList()))).stream().collect(Collectors.groupingBy(User::getUid));
        records.forEach(e -> {
            InvoicePageResponse response = new InvoicePageResponse();
            BeanUtils.copyProperties(e, response);
            response.setUserName(userList.get(e.getUid()).get(0).getNickname());
            response.setStatus(ObjectUtil.isEmpty(e.getUrl()) ? 0 : e.getStatus());
            responses.add(response);
        });
        if (ObjectUtil.isNotEmpty(request.getStatus())){
            return PageUtils.startPage(responses.stream()
                    .filter(r -> r.getStatus().equals(Integer.parseInt(request.getStatus())))
                    .sorted(Comparator.comparing(InvoicePageResponse::getCreateTime).reversed())
                    .collect(Collectors.toList()), request.getPage(), request.getLimit());
        }
        return PageUtils.startPage(responses.stream().sorted(Comparator.comparing(InvoicePageResponse::getCreateTime).reversed()).collect(Collectors.toList()), request.getPage(), request.getLimit());
    }

    @Override
    public List<InvoiceOrderResponse> invoiceOrder(Integer id) {
        List<InvoiceOrderResponse> responses = new ArrayList<>();
        List<InvoiceOrderRelation> orderRelations = invoiceOrderRelationService.list(Wrappers.<InvoiceOrderRelation>lambdaQuery().in(InvoiceOrderRelation::getInvoiceId, id));
        List<StoreOrder> orders = orderService.list(Wrappers.<StoreOrder>lambdaQuery().in(StoreOrder::getId, orderRelations.stream().map(InvoiceOrderRelation::getOrderId).collect(Collectors.toList())));
        orders.forEach(e -> {
            InvoiceOrderResponse response = new InvoiceOrderResponse();
            response.setOrderNo(e.getOrderId());
            response.setPrice(e.getPayPrice());
            response.setCreateTime(e.getCreateTime());
            List<StoreOrderInfo> infos = orderInfoService.list(Wrappers.<StoreOrderInfo>lambdaQuery().eq(StoreOrderInfo::getOrderNo, e.getOrderId()));
            List<PreOrderCommonVo.PreOrderProductDetail> orderProductDetail = new ArrayList<>();
            infos.forEach(f -> {
                PreOrderCommonVo.PreOrderProductDetail preOrderProductDetail = new PreOrderCommonVo.PreOrderProductDetail();
                BeanUtils.copyProperties(f, preOrderProductDetail);
                preOrderProductDetail.setBuyNum(f.getPayNum());
                orderProductDetail.add(preOrderProductDetail);
            });
            response.setOrderProductDetail(orderProductDetail);
            responses.add(response);
        });
        return responses;
    }

    @Override
    public JSONObject invoiceResultDownload(String json) {
        log.info("json-------------->{}", json);
        JSONObject jsonObject = JSON.parseObject(json);
        String code = jsonObject.get("code").toString();
        ThirdPartyExceptions thirdPartyExceptions =null;
        if (code.equals("1")){
            thirdPartyExceptions = new ThirdPartyExceptions();
            thirdPartyExceptions.setErrcode(code);
            thirdPartyExceptions.setErrmsg(jsonObject.get("message").toString());
            thirdPartyExceptions.setData(json);
            thirdPartyExceptions.setRemark("xforce回调异常");
            thirdPartyExceptions.setType(3);
        }
        String orderType = jsonObject.get("orderType").toString();
        JSONObject data = JSON.parseObject(jsonObject.get("data").toString());
        String orderId = jsonObject.get("orderId").toString();
        InvoiceRecord record = this.getOne(Wrappers.<InvoiceRecord>lambdaQuery().eq(InvoiceRecord::getInvoiceOrderId, orderId));
        List<InvoiceOrderRelation> relations = invoiceOrderRelationService.list(Wrappers.<InvoiceOrderRelation>lambdaQuery().eq(InvoiceOrderRelation::getInvoiceId, record.getId()));
        List<InvoiceCreditNote> resultInvoice = new ArrayList<>();
        if ((!record.getStatus().equals(2)) && "SALE".equals(orderType)) {
            if (ObjectUtil.isEmpty(data.get("sellerInvoiceMain"))) {
                record.setStatus("0".equals(code) ? 1 : 0);
            } else {
                JSONObject sellerInvoiceMain = JSON.parseObject(JSON.parseObject(data.toString()).get("sellerInvoiceMain").toString());
                String pdfUrl = sellerInvoiceMain.get("pdfUrl").toString();
                record.setUrl(pdfUrl);
            }
        } else if ("RTRN".equals(orderType)) {
            if (ObjectUtil.isNotEmpty(data.get("sellerInvoiceDetails"))) {
                JSONArray sellerInvoiceDetails = JSON.parseArray(JSON.parseObject(data.toString()).get("sellerInvoiceDetails").toString());
                JSONObject sellerInvoiceMain = JSON.parseObject(JSON.parseObject(data.toString()).get("sellerInvoiceMain").toString());
                String pdfUrl = sellerInvoiceMain.get("pdfUrl").toString();
                List<String> orderInfoIds = new ArrayList<>();
                sellerInvoiceDetails.forEach(e -> {
                    orderInfoIds.add(JSON.parseObject(e.toString()).get("orderItemNo").toString());
                });
                List<StoreOrderInfo> orderInfos = orderInfoService.list(Wrappers.<StoreOrderInfo>lambdaQuery().in(StoreOrderInfo::getId, orderInfoIds));
                List<InvoiceCreditNote> creditNotes = invoiceCreditNoteService.list(Wrappers.<InvoiceCreditNote>lambdaQuery().in(InvoiceCreditNote::getId, orderInfos.stream().map(StoreOrderInfo::getCreditNoteId).collect(Collectors.toList())));
                creditNotes.forEach(c -> {
                    c.setUrl(pdfUrl);
                });
                resultInvoice.addAll(creditNotes);
                InvoiceRecord invoiceRecord = this.getOne(Wrappers.<InvoiceRecord>lambdaQuery().eq(InvoiceRecord::getInvoiceOrderId, orderId));
                if (!record.getOrderInfoTotal().equals(0)){
                    int num = record.getOrderInfoTotal() - sellerInvoiceDetails.size();
                    record.setOrderInfoTotal(record.getOrderInfoTotal()>=sellerInvoiceDetails.size()?num:0);
                }
                if (record.getOrderInfoTotal().equals(0)){
                    record.setStatus(2);
                }
            }
        }
        ThirdPartyExceptions finalThirdPartyExceptions = thirdPartyExceptions;
        Boolean execute = transactionTemplate.execute(e -> {
            if (ObjectUtil.isNotEmpty(finalThirdPartyExceptions)){
                thirdPartyExceptionsService.save(finalThirdPartyExceptions);
            }
            if ("SALE".equals(orderType)) {
                if (record.getStatus().equals(2)) {
                    orderService.update(Wrappers.<StoreOrder>lambdaUpdate().in(StoreOrder::getId, relations.stream().map(InvoiceOrderRelation::getOrderId).collect(Collectors.toList())).set(StoreOrder::getInvoicingStatus, 0));
                }
            } else if ("RTRN".equals(orderType)) {
                invoiceCreditNoteService.updateBatchById(resultInvoice);
            }
            this.updateById(record);
            return Boolean.TRUE;
        });
        return execute ? JSON.parseObject("{\"_tstamp\":\"" + DateUtil.nowDate(Constants.DATE_FORMAT) + "\",\"code\":\"1\",\"errMsg\":\"成功\"}")
                : JSON.parseObject("{\"_tstamp\":\"" + DateUtil.nowDate(Constants.DATE_FORMAT) + "\",\"code\":\"0\",\"errMsg\":\"失败\"}");
    }

    /**
     * @param merOrderNo
     * @return {@link Boolean}
     * @description 红冲发票
     * @author Li
     * @date 2023/2/14 9:35
     */
    @Override
    @SneakyThrows()
    public Boolean creditNote(List<String> merOrderNo) {
        HashMap<String, String> info = systemConfigService.info(168);
        //查询子订单
        List<StoreOrderInfo> orderInfos = orderInfoService.list(
                Wrappers.<StoreOrderInfo>lambdaQuery()
                        .in(StoreOrderInfo::getMerOrderNo, merOrderNo)
                        .ge(StoreOrderInfo::getPayPrice, new BigDecimal("0.1")));
        //查询父订单
        List<StoreOrder> orders = orderService.list(
                Wrappers.<StoreOrder>lambdaQuery()
                        .in(StoreOrder::getOrderId, orderInfos.stream().map(StoreOrderInfo::getOrderNo).distinct().collect(Collectors.toList())));
        //查询发票和订单关系
        InvoiceOrderRelation relation = invoiceOrderRelationService.getOne(
                Wrappers.<InvoiceOrderRelation>lambdaQuery()
                        .in(InvoiceOrderRelation::getOrderId, orders.stream().map(StoreOrder::getId).collect(Collectors.toList()))
                        .orderByDesc(InvoiceOrderRelation::getCreateTime).last("limit 1"));
        //查询发票信息
        InvoiceRecord record = this.getOne(
                Wrappers.<InvoiceRecord>lambdaQuery()
                        .eq(InvoiceRecord::getId, relation.getInvoiceId()));

        XforceVo xforce = getXforce(orderInfos,orders,2);
        xforce.setOrderInfoNo(record.getInvoiceOrderId());
        xforce.setStoreCode(info.get("storeCode"));
        xforce.setBrand(ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish("lecole")));
        xforce.setPosDate(DateUtil.nowDate());
        xforce.setOrderType("RTRN");
        xforce.setInvoiceType("ce");
        xforce.setBusinessBillType("AR");
        xforce.setStatus("1");
        xforce.setSellerInfoFill("0");
        xforce.setPurchaserInfoFill("1");
        xforce.setEmail(ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish(record.getEmail())));
        xforce.setPurchaserTaxNo(record.getType().equals(2) ? record.getCompanyTaxNumber() : "");
        xforce.setPurchaserName(record.getType().equals(2) ? ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish(record.getCorporateName())) : ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish(record.getName())));
        JSONObject object = new JSONObject();
        object.put("mainInfo", JSON.parseObject(JSON.toJSONString(xforce)));
        log.info("xforce----------------------->{}", object);
        JSONObject jsonObject = JSON.parseObject(restTemplateUtil.postAuthData(url+SETTLEMENT_UPLOAD, object, info.get("userName"), info.get("password")));
        log.info("jsonObject----------------------->{}", jsonObject);
        String code = jsonObject.get("code").toString();
        String status = jsonObject.get("status").toString();
        if (!"0".equals(code)) {
            return Boolean.FALSE;
        }
        Map<String, List<StoreOrderInfo>> listMap = orderInfoService.list(
                        Wrappers.<StoreOrderInfo>lambdaQuery()
                                .in(StoreOrderInfo::getOrderNo, orders.stream().map(StoreOrder::getOrderId).collect(Collectors.toList()))
                                .ge(StoreOrderInfo::getPayPrice, new BigDecimal("0.1")))
                .stream().collect(Collectors.groupingBy(StoreOrderInfo::getOrderNo));
        orders.forEach(e -> {
            int count = 0;
            for (int i = 0; i < listMap.get(e.getOrderId()).size(); i++) {
                count += listMap.get(e.getOrderId()).get(i).getRefundStatus().equals(2) ? 1 : 0;
            }
            e.setInvoicingStatus(count == listMap.get(e.getOrderId()).size() ? 0 : 1);
        });

        InvoiceCreditNote invoiceCreditNote = new InvoiceCreditNote();
        invoiceCreditNote.setInvoiceId(record.getId());
        invoiceCreditNote.setPrice(xforce.getPayAmount());
        return transactionTemplate.execute(e -> {
            orderService.updateBatchById(orders);
            invoiceCreditNoteService.save(invoiceCreditNote);
            orderInfoService.update(Wrappers.<StoreOrderInfo>lambdaUpdate()
                    .in(StoreOrderInfo::getId, orderInfos.stream().map(StoreOrderInfo::getId).collect(Collectors.toList()))
                    .set(StoreOrderInfo::getCreditNoteId, invoiceCreditNote.getId()));
            this.update(Wrappers.<InvoiceRecord>lambdaUpdate().eq(InvoiceRecord::getId, record.getId()).set(InvoiceRecord::getCreditNote, 1));
            return Boolean.TRUE;
        });
    }

    /**
     * 获取request的where条件
     *
     * @param queryWrapper QueryWrapper<StoreOrder> 表达式
     * @param request      StoreOrderSearchRequest 请求参数
     */
    private void getRequestTimeWhere(LambdaQueryWrapper<InvoiceRecord> queryWrapper, InvoicePageRequest request) {
        if (StringUtils.isNotBlank(request.getDateLimit())) {
            dateLimitUtilVo dateLimitUtilVo = DateUtil.getDateLimit(request.getDateLimit());
            queryWrapper.between(InvoiceRecord::getCreateTime, dateLimitUtilVo.getStartTime().substring(0, 10), dateLimitUtilVo.getEndTime().substring(0, 10)+ " 23:59:59");
        }
    }

    public XforceVo getXforce(List<StoreOrderInfo> infos,List<StoreOrder> orders,Integer type) throws UnsupportedEncodingException {
        Map<String, List<StoreOrderInfo>> collect = infos.stream().collect(Collectors.groupingBy(StoreOrderInfo::getOrderNo));
        List<Integer> productList = infos.stream().filter(e -> e.getType().equals(4))
                .map(StoreOrderInfo::getProductId).collect(Collectors.toList());
        Map<Integer, VcaProduct> toMap =new HashMap<>();
        if (ObjectUtil.isNotEmpty(productList)){
            toMap.putAll(VcaUtil.listToMap(vcaProductService.list(
                    Wrappers.<VcaProduct>lambdaQuery()
                            .in(VcaProduct::getId, productList)), "id", Integer.class));
        }
        List<OrderGroupByAttrVo> groupByAttrs = new ArrayList<>();
        Map<String, StoreOrder> listToMap = VcaUtil.listToMap(orders, "orderId", String.class);
        List<BigDecimal> outerDiscountWithTax=new ArrayList<>();
        collect.forEach((k,v)->{
            BigDecimal couponPrice = listToMap.get(k).getCouponPrice();
            List<OrderGroupByAttrVo> vos = VcaUtil.copyList(v, OrderGroupByAttrVo.class);
            if (ObjectUtil.isNotEmpty(couponPrice)){
                outerDiscountWithTax.add(couponPrice);
                BigDecimal divide = couponPrice.divide(BigDecimal.valueOf(v.size()), RoundingMode.HALF_UP);
                vos.forEach(e->e.setOuterDiscountWithTax(divide.toString()));
            }
            groupByAttrs.addAll(vos);
        });
        XforceVo xforce = new XforceVo();
        List<BigDecimal> compare = new ArrayList<>();
        List<DetailInfoVo> list=new ArrayList<>();
        List<HashMap<String, Object>> maps = systemGroupDataService.getListMapByGidAll("",102, false);
        Map<String, Map> typeMap = listMapToMap(maps,"type");
        Map<String, Map> productMap = listMapToMap(maps,"product_type");
        for (OrderGroupByAttrVo e : groupByAttrs) {
            if (new BigDecimal(e.getPrice()).compareTo(new BigDecimal("0.09")) > 0) {
                DetailInfoVo detailInfoVo = null;
                if (ObjectUtil.isNotEmpty(typeMap)){
                    if (ObjectUtil.isNotEmpty(typeMap.get(e.getType()))) {
                        detailInfoVo=getTax(typeMap.get(e.getType()));
                    }else {
                        throw new VcaException("开票商品缺少税编、税率，请联系客服");
                    }
                }else {
                    throw new VcaException("开票商品缺少税编、税率，请联系客服");
                }
                if (ObjectUtil.isNotEmpty(productMap)){
                    if (ObjectUtil.isNotEmpty(toMap.get(Integer.valueOf(e.getProductId())))){
                        if (toMap.get(Integer.valueOf(e.getProductId())).getCid().equals(750)){
                            Integer useAround = toMap.get(Integer.valueOf(e.getProductId())).getUseAround();
                            if (useAround ==0){
                                useAround =0;
                            } else if (useAround==1){
                                useAround =2;
                            }else if (useAround==2){
                                useAround =3;
                            }else {
                                throw new VcaException("开票商品缺少税编、税率，请联系客服");
                            }
                            detailInfoVo=getTax(typeMap.get(useAround.toString()));

                        }else if (ObjectUtil.isNotEmpty(productMap.get(toMap.get(Integer.valueOf(e.getProductId())).getCid().toString()))) {
                            detailInfoVo=getTax(productMap.get(toMap.get(Integer.valueOf(e.getProductId())).getCid().toString()));
                        }else {
                            throw new VcaException("开票商品缺少税编、税率，请联系客服");
                        }
                    }
                }
                //订单编号
                detailInfoVo.setOrderItemNo(ChangeCharsetUtil.toGBK(e.getId()));
                //取出商品数量
                String payNum = e.getPayNum();
                //明细名称
                detailInfoVo.setItemName(ChangeCharsetUtil.toGBK(ChangeCharsetUtil.frenchToEnglish(e.getName())));
                String productId = e.getProductId();
                if ("0".equals(productId)) {
                    String code = e.getSchedulingId() +
                            e.getMainId() +
                            e.getType();
                    //商品编码
                    detailInfoVo.setVolunCode(ChangeCharsetUtil.toGBK(code));
                    //商品规格
                    detailInfoVo.setItemSpec(ChangeCharsetUtil.toGBK("default"));
                } else {
                    //商品编码
                    detailInfoVo.setVolunCode(ChangeCharsetUtil.toGBK(e.getProductId()));
                    //商品规格
                    detailInfoVo.setItemSpec(ChangeCharsetUtil.toGBK(e.getSku()));
                }
                //商品数量
                detailInfoVo.setQuantity(new BigDecimal(payNum));
                //单价
                detailInfoVo.setUnitPrice(e.getPrice());
                //已分摊折扣
                detailInfoVo.setOuterDiscountWithTax("0");
                //含税金额 数量*单价
                detailInfoVo.setAmountWithTax(new BigDecimal(payNum).multiply(new BigDecimal(e.getPrice())));
                //开票标示 默认0-开票，1-不开票
                detailInfoVo.setLegalInvoiceFlag("0");
                list.add(detailInfoVo);
            } else {
                compare.add(new BigDecimal(e.getPayPrice()));
            }
        }
        xforce.setDetailInfo(list);
        //取出发票总价
        List<BigDecimal> prices = orders.stream().map(StoreOrder::getPayPrice).collect(Collectors.toList());
        BigDecimal price = prices.stream().reduce(BigDecimal.ZERO, BigDecimal::add).subtract(compare.stream().reduce(BigDecimal.ZERO, BigDecimal::add));
        xforce.setAmountWithTax(price);
        if (type==1){
            xforce.setPayAmount(price);
        }else if (type==2){
            xforce.setPayAmount(price.negate());
        }
        xforce.setOuterDiscountWithTax(outerDiscountWithTax.stream().reduce(BigDecimal.ZERO, BigDecimal::add));
        return xforce;
    }

    public Map<String,Map> listMapToMap(List<HashMap<String, Object>> listMap,String key){
        Map<String,Map> map=new HashMap<>();
        listMap.forEach(e->{
            Map m = JSON.parseObject(JSON.toJSONString(e), Map.class);
            if (ObjectUtil.isNotEmpty(m.get(key))){
                map.put(m.get(key).toString(),m);
            }
        });
        return map;
    }

    public DetailInfoVo getTax(Map map){
        DetailInfoVo detailInfoVo = new DetailInfoVo();
        //数量单位
        try {
            detailInfoVo.setQuantityUnit(ChangeCharsetUtil.toGBK(map.get("unit").toString()));
            //税率
            detailInfoVo.setTaxRate(new BigDecimal(map.get("tax_rate").toString()));
            //商品税编
            detailInfoVo.setItemCode(map.get("tax_code").toString());
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new VcaException("开票商品缺少税编、税率，请联系客服");
        }
        return detailInfoVo;
    }

    public Boolean rollback(InvoiceRecord invoiceRecord,List<InvoiceOrderRelation> relations ,List<StoreOrder> orders){
        return transactionTemplate.execute(e -> {
            this.removeById(invoiceRecord.getId());
            invoiceOrderRelationService.removeByIds(relations.stream().map(InvoiceOrderRelation::getId).collect(Collectors.toList()));
            orderService.update(Wrappers.<StoreOrder>lambdaUpdate().in(StoreOrder::getId, orders.stream().map(StoreOrder::getId).collect(Collectors.toList())).set(StoreOrder::getInvoicingStatus, 0));
            return Boolean.TRUE;
        });
    }
}
